#
#   KRATOS _______
#         / ____(_)___ ____  ____
#        / __/ / / __ `/ _ \/ __ \
#       / /___/ / /_/ /  __/ / / /
#      /_____/_/\__, /\___/_/ /_/ SolversApplication
#              /____/
#
#   Author: Thomas Oberbichler
#

add_library(KratosLinearSolversCore SHARED)

set( CMAKE_INCLUDE_CURRENT_DIR ON )

message( "**** configuring KratosLinearSolversApplication ****" )

# Options.
option(USE_EIGEN_MKL
       "Compile the LinearSolversApplication with support for solvers from Intel MKL."
       OFF)

option(USE_EIGEN_SUITESPARSE
       "Compile the LinearSolversApplication with support for solvers from SuiteSparse."
       OFF)

################### PYBIND11
include(pybind11Tools)

target_compile_definitions(KratosLinearSolversCore PUBLIC
                           EIGEN_MPL2_ONLY)
target_include_directories(KratosLinearSolversCore
                           PUBLIC "${KRATOS_SOURCE_DIR}/kratos"
                           SYSTEM PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/external_libraries/eigen3"
                                         "${CMAKE_CURRENT_SOURCE_DIR}/external_libraries/spectra1/include"
                           INTERFACE "$<INSTALL_INTERFACE:include/kratos>"
                                     "$<BUILD_INTERFACE:${KRATOS_SOURCE_DIR}/kratos>"
                           SYSTEM INTERFACE "$<INSTALL_INTERFACE:include/kratos/applications/LinearSolversApplication/external_libraries/eigen3>"
                                            "$<INSTALL_INTERFACE:include/kratos/applications/LinearSolversApplication/external_libraries/spectra1/include>"
                                            "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/external_libraries/eigen3>"
                                            "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/external_libraries/spectra1/include>")

if( USE_EIGEN_MKL MATCHES ON )
    if( DEFINED ENV{MKLROOT} )
        message( "-- MKLROOT = $ENV{MKLROOT}" )

        target_include_directories(KratosLinearSolversCore SYSTEM PUBLIC "$ENV{MKLROOT}/include")
        target_link_directories(KratosLinearSolversCore PUBLIC "$ENV{MKLROOT}/lib")

        if( NOT MSVC )
            target_compile_options(KratosLinearSolversCore PUBLIC "SHELL:${CMAKE_CXX_FLAGS}" "-m64" "-L$ENV{MKLROOT}/lib/intel64" "-Wl,--no-as-needed" "-lpthread" "-lm" "-ldl")
        endif()
    elseif( DEFINED ENV{CONDA_PREFIX} )
        message("-- Found Conda environment: $ENV{CONDA_PREFIX}")

        if( WIN32 )
            target_include_directories(KratosLinearSolversCore SYSTEM PUBLIC "$ENV{CONDA_PREFIX}/Library/include")
            target_link_directories(KratosLinearSolversCore PUBLIC "$ENV{CONDA_PREFIX}/Library/lib")
        else()
            target_include_directories(KratosLinearSolversCore SYSTEM PUBLIC "$ENV{CONDA_PREFIX}/include")
            target_link_directories(KratosLinearSolversCore PUBLIC "$ENV{CONDA_PREFIX}/lib")
        endif()
    else()
        message( FATAL_ERROR "MKLROOT not defined" )
    endif()

    target_compile_definitions(KratosLinearSolversCore PUBLIC
                               USE_EIGEN_MKL EIGEN_USE_MKL_ALL)
    target_sources(KratosLinearSolversCore PRIVATE
                   "${CMAKE_CURRENT_SOURCE_DIR}/custom_solvers/mkl_smoother_base.cpp"
                   "${CMAKE_CURRENT_SOURCE_DIR}/custom_solvers/mkl_ilu.cpp"
                   "${CMAKE_CURRENT_SOURCE_DIR}/custom_utilities/mkl_utilities.cpp"
                   )
endif()

if( USE_EIGEN_FEAST MATCHES ON )
    if( WIN32 )
        message( FATAL_ERROR "FEAST4 is not yet supported under Windows")
    endif()
    if( USE_EIGEN_MKL MATCHES OFF OR NOT DEFINED USE_EIGEN_MKL )
        message( FATAL_ERROR "FEAST4 can only be used with MKL" )
    endif()
    include(CMakeAddFortranSubdirectory)
    cmake_add_fortran_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/external_libraries/FEAST NO_EXTERNAL_INSTALL )
    set(FEAST_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/external_libraries/FEAST/4.0 )

    target_include_directories(KratosLinearSolversCore
                               SYSTEM PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/external_libraries/FEAST/4.0/include"
                               SYSTEM INTERFACE "$<INSTALL_INTERFACE:include/kratos/applications/LinearSolversApplication/external_libraries/FEAST/4.0/include>"
                                                "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/external_libraries/FEAST/4.0/include")
    target_compile_definitions(KratosLinearSolversCore PUBLIC "USE_EIGEN_FEAST")

    # MKL implements some of FEAST's function. This definition excludes the definitions of MKL's FEAST
    # functions in mkl/include/mkl_solvers_ee.h. Otherwise, some functions are defined multiple times
    # defining both as MKL uses either, depending on the version
    target_compile_definitions(KratosLinearSolversCore PUBLIC "_MKL_SOLVERS_EE_H" "__MKL_SOLVERS_EE_H")
endif()

if (USE_EIGEN_SUITESPARSE)
    include("${CMAKE_CURRENT_SOURCE_DIR}/external_libraries/eigen3/cmake/FindCHOLMOD.cmake")
    include("${CMAKE_CURRENT_SOURCE_DIR}/external_libraries/eigen3/cmake/FindSPQR.cmake")
    include("${CMAKE_CURRENT_SOURCE_DIR}/external_libraries/eigen3/cmake/FindUMFPACK.cmake")
    target_compile_definitions(KratosLinearSolversCore PUBLIC KRATOS_USE_EIGEN_SUITESPARSE)
endif()

## generate variables with the sources
target_sources(KratosLinearSolversCore PRIVATE
               ${CMAKE_CURRENT_SOURCE_DIR}/linear_solvers_application.cpp
               ${CMAKE_CURRENT_SOURCE_DIR}/custom_factories/dense_linear_solver_factory.cpp)

# Sources for the Python module
file(
    GLOB_RECURSE
    KRATOS_LINEARSOLVERS_APPLICATION_PYTHON_INTERFACE
    ${CMAKE_CURRENT_SOURCE_DIR}/custom_python/*.cpp
)

###############################################################
## LinearSolversApplication core library (C++ parts)
target_link_libraries(KratosLinearSolversCore PUBLIC KratosCore)
target_compile_definitions(KratosLinearSolversCore PRIVATE "LINEARSOLVERS_APPLICATION=EXPORT,API")

## LinearSolversApplication python module
pybind11_add_module(KratosLinearSolversApplication MODULE THIN_LTO ${KRATOS_LINEARSOLVERS_APPLICATION_PYTHON_INTERFACE} )
target_link_libraries(KratosLinearSolversApplication PRIVATE KratosLinearSolversCore )
set_target_properties(KratosLinearSolversApplication PROPERTIES PREFIX "")

# Set batch size in the unity build
IF(CMAKE_UNITY_BUILD MATCHES ON)
    set_target_properties(KratosLinearSolversCore PROPERTIES UNITY_BUILD_BATCH_SIZE ${KRATOS_UNITY_BUILD_BATCH_SIZE})
    set_target_properties(KratosLinearSolversApplication PROPERTIES UNITY_BUILD_BATCH_SIZE ${KRATOS_UNITY_BUILD_BATCH_SIZE})
ENDIF(CMAKE_UNITY_BUILD MATCHES ON)

if( USE_EIGEN_MKL MATCHES ON )
    if( MSVC )
        find_library(MKL_RT_LIB mkl_rt)
        if(NOT MKL_RT_LIB)
            message( FATAL_ERROR "mkl_rt.lib not found")
        else(NOT MKL_RT_LIB)
            message( "mkl_rt.lib found at: ${MKL_RT_LIB}")
            target_link_libraries(KratosLinearSolversCore PUBLIC ${MKL_RT_LIB} )
        endif(NOT MKL_RT_LIB)
    else( MSVC )
        target_link_libraries(KratosLinearSolversCore PUBLIC mkl_rt)
        if( USE_EIGEN_FEAST MATCHES ON )
            target_link_libraries(KratosLinearSolversCore PUBLIC feast4 gfortran m)
        endif()
    endif( MSVC )
endif()

if (USE_EIGEN_SUITESPARSE)
    target_link_libraries(KratosLinearSolversCore PUBLIC
                          ${CHOLMOD_LIBRARIES}
                          ${SPQR_LIBRARIES}
                          ${UMFPACK_LIBRARIES})
    target_include_directories(KratosLinearSolversCore PUBLIC
                               ${CHOLMOD_INCLUDES}
                               ${SPQR_INCLUDES}
                               ${UMFPACK_INCLUDES})
endif()

# changing the .dll suffix to .pyd
if( ${CMAKE_SYSTEM_NAME} MATCHES "Windows" )
    set_target_properties( KratosLinearSolversCore PROPERTIES SUFFIX .pyd )
    set_target_properties( KratosLinearSolversApplication PROPERTIES SUFFIX .pyd )
endif( ${CMAKE_SYSTEM_NAME} MATCHES "Windows" )

# Add to the KratosMultiphisics Python module
kratos_python_install(${INSTALL_PYTHON_USING_LINKS} ${CMAKE_CURRENT_SOURCE_DIR}/LinearSolversApplication.py KratosMultiphysics/LinearSolversApplication/__init__.py )

# Install python files
get_filename_component (CURRENT_DIR_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
kratos_python_install_directory(${INSTALL_PYTHON_USING_LINKS} ${CMAKE_CURRENT_SOURCE_DIR}/python_scripts KratosMultiphysics/${CURRENT_DIR_NAME} )

# Kratos Testing. Install everything except sources to ensure that reference and configuration files are copied.
if(${INSTALL_TESTING_FILES} MATCHES ON )
    get_filename_component (CURRENT_DIR_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME)
    install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests DESTINATION applications/${CURRENT_DIR_NAME}
        PATTERN "*.git" EXCLUDE
        PATTERN "*.c" EXCLUDE
        PATTERN "*.h" EXCLUDE
        PATTERN "*.cpp" EXCLUDE
        PATTERN "*.hpp" EXCLUDE
  )
endif(${INSTALL_TESTING_FILES} MATCHES ON)

# Install targets
install(TARGETS KratosLinearSolversCore DESTINATION libs)
install(TARGETS KratosLinearSolversApplication DESTINATION libs)

# Define custom targets
set(KRATOS_KERNEL "${KRATOS_KERNEL};KratosLinearSolversCore" PARENT_SCOPE)
set(KRATOS_PYTHON_INTERFACE "${KRATOS_PYTHON_INTERFACE};KratosLinearSolversApplication" PARENT_SCOPE)
